package com.http.server;

import com.common.constant.CommonConstant;
import com.common.util.CommonUtils;
import com.http.handler.HttpHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Properties;

public class HttpServer {

    private final static Logger logger = LoggerFactory.getLogger(HttpServer.class);
    private int port;
    private String host;



    public HttpServer() {
        Properties prop = CommonUtils.getProperty();
        this.host = StringUtils.isNoneBlank(prop.getProperty(CommonConstant.HTTP_HOST_KEY)) ? prop.getProperty(CommonConstant.HTTP_PORT_KEY) : CommonConstant.DEFAULT_HTTP_HOST;
        String strPort = prop.getProperty(CommonConstant.HTTP_PORT_KEY);
        this.port = StringUtils.isNoneBlank(strPort) ? Integer.parseInt(strPort) : CommonConstant.DEFAULT_HTTP_PORT;
    }


    private void readProperties() throws IOException {
        Properties prop = CommonUtils.getProperty();
        String host = prop.getProperty(CommonConstant.HTTP_HOST_KEY);
        String port = prop.getProperty(CommonConstant.HTTP_PORT_KEY);
        logger.info("host:" + host + " port:" + port);
    }

    public static void main(String[] args) throws Exception {
        new HttpServer().readProperties();
        new HttpServer().start();
    }



    public void start() throws Exception {
        long start = System.currentTimeMillis();

        EventLoopGroup boss = null;
        EventLoopGroup worker = null;
        boolean isLinux = CommonUtils.isLinux();
        if (isLinux) {
            boss = new EpollEventLoopGroup();
            worker = new EpollEventLoopGroup();
        } else {
            boss = new NioEventLoopGroup();
            worker = new NioEventLoopGroup();
        }
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker);
            bootstrap.channel(isLinux ? EpollServerSocketChannel.class : NioServerSocketChannel.class);
//            设置tcp缓冲区
            bootstrap.option(ChannelOption.SO_BACKLOG, 128);
//            SO_RCVBUF这两个参数用于操作接收缓冲区
//            bootstrap.option(ChannelOption.SO_RCVBUF, 10*1024*1024);
////          SO_SNDBUF发送缓冲区
//            bootstrap.option(ChannelOption.SO_SNDBUF, 1024*1024);

            //通过NoDelay禁用Nagle,使消息立即发出去，不用等待到一定的数据量才发出去
//            bootstrap.option(ChannelOption.TCP_NODELAY, true);
            /**
             * 1、FixedRecvByteBufAllocator：固定长度的接收缓冲区分配器，由它分配的ByteBuf长度都是固定大小的，
             * 并不会根据实际数据报的大小动态收缩。但是，如果容量不足，支持动态扩展。
             * 动态扩展是Netty ByteBuf的一项基本功能，与ByteBuf分配器的实现没有关系；
             * 2、AdaptiveRecvByteBufAllocator：容量动态调整的接收缓冲区分配器，它会根据之前Channel接收到的数据报大小进行计算，
             * 如果连续填充满接收缓冲区的可写空间，则动态扩展容量。如果连续2次接收到的数据报都小于指定值，
             * 则收缩当前的容量，以节约内存。
             */
            bootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT);
            //保持长连接状态
            bootstrap.childOption(ChannelOption.SO_KEEPALIVE, false);
            bootstrap.handler(new LoggingHandler(LogLevel.INFO));
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    ChannelPipeline p = socketChannel.pipeline();
                    p.addLast(new HttpServerCodec());
                    p.addLast(new HttpObjectAggregator(64 * 1024));
//                    p.addLast(new JsonDecode());
//                    p.addLast(new JsonEncode());
//                    p.addLast(new HttpServerHandler());

                    p.addLast(new HttpHandler());
//                    p.addLast(new ObjectEncoder());
//                    p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
//                    p.addFirst(new SocketHandler());
//                    p.addLast(new EchoMessageDecoder());
//                    p.addLast(new EchoMessageEncoder());

                }
            });
            ChannelFuture f = bootstrap.bind(port).sync();
            if (f.isSuccess()) {
                logger.info("Server started:" + (System.currentTimeMillis() - start) + " ms");
            }
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            logger.error("[ERROR--HttpServer]:" + e.fillInStackTrace());

        } finally {
            boss.shutdownGracefully().sync();
            worker.shutdownGracefully().sync();
        }
    }


}
